package utils

/*
仿照Java/JavaScript的Stream接口设计的函数
因为Golang中不支持方法泛型，所以是用函数实现。
因为Golang中不能省略参数类型，所以直接用并不是很方便，但如果h是一个已经存在的函数，则用起来会方便很多。
*/

func Map[T1, T2 any](data []T1, h func(v T1) T2) []T2 {
	res := make([]T2, 0, len(data))
	for _, v := range data {
		res = append(res, h(v))
	}
	return res
}

func MapWithIndex[T1, T2 any](data []T1, h func(i int, v T1) T2) []T2 {
	res := make([]T2, 0, len(data))
	for i, v := range data {
		res = append(res, h(i, v))
	}
	return res
}

func MapAndFilter[T1, T2 any](data []T1, h func(v T1) (T2, bool)) []T2 {
	res := make([]T2, 0)
	for _, v := range data {
		if _v, ok := h(v); ok {
			res = append(res, _v)
		}
	}
	return res
}

func FlatMap[T1, T2 any](data []T1, h func(v T1) []T2) []T2 {
	res := make([]T2, 0, len(data))
	for _, v := range data {
		res = append(res, h(v)...)
	}
	return res
}

func Reduce[Elem, Res any](data []Elem, res Res, h func(v Elem, res Res) Res) Res {
	for _, v := range data {
		res = h(v, res)
	}
	return res
}

func ReduceToMap[Elem any, Key comparable, Value any](data []Elem, h func(v Elem, i int) (Key, Value)) map[Key]Value {
	res := map[Key]Value{}
	for index, item := range data {
		k, v := h(item, index)
		res[k] = v
	}
	return res
}

func ReduceToMapX[Elem any, Key comparable](data []Elem, h func(v Elem, i int) Key) map[Key]Elem {
	res := map[Key]Elem{}
	for index, item := range data {
		k := h(item, index)
		res[k] = item
	}
	return res
}

func ReduceToMapSlice[Elem any, Key comparable, Value any](data []Elem, h func(v Elem) (Key, Value)) map[Key][]Value {
	res := map[Key][]Value{}
	for _, item := range data {
		k, v := h(item)
		if res[k] == nil {
			res[k] = []Value{v}
		} else {
			res[k] = append(res[k], v)
		}
	}
	return res
}

func Filter[Elem any](data []Elem, h func(v Elem) bool) []Elem {
	res := make([]Elem, 0)
	for _, v := range data {
		if h(v) {
			res = append(res, v)
		}
	}
	return res
}

func Has[Elem any](data []Elem, h func(v Elem) bool) bool {
	for _, v := range data {
		if h(v) {
			return true
		}
	}
	return false
}

func ToAny[T any](v T) any {
	return v
}

func Foreach[T any](s []T, h func(v *T)) []T {
	for i := range s {
		h(&s[i])
	}
	return s
}
